﻿// Fill out your copyright notice in the Description page of Project Settings.


#include "SmartMovieScene3DTransformSectionExtensions.h"
#include "Editor/UnrealEd/Public/FbxImporter.h"
#include "MovieSceneTools/Public/MovieSceneToolHelpers.h"
// #include "FbxImporter.h"
#include "MatineeImportTools.h"
#include "Channels/MovieSceneChannelProxy.h"

// #if ENGINE_MINOR_VERSION > 21
#if ENGINE_MINOR_VERSION < 21
static void ImportTransformChannel(const FRichCurve& Source, FMovieSceneFloatChannel* Dest, FFrameRate DestFrameRate, bool bNegateTangents)
{
	TMovieSceneChannelData<FMovieSceneFloatValue> ChannelData = Dest->GetData();
	ChannelData.Reset();
	double DecimalRate = DestFrameRate.AsDecimal();
	for (int32 KeyIndex = 0; KeyIndex < Source.Keys.Num(); ++KeyIndex)
	{
		float ArriveTangent = Source.Keys[KeyIndex].ArriveTangent;
		if (KeyIndex > 0)
		{
			ArriveTangent = ArriveTangent / ((Source.Keys[KeyIndex].Value - Source.Keys[KeyIndex - 1].Value) * DecimalRate);
		}

		float LeaveTangent = Source.Keys[KeyIndex].LeaveTangent;
		if (KeyIndex < Source.Keys.Num() - 1)
		{
			LeaveTangent = LeaveTangent / ((Source.Keys[KeyIndex + 1].Value - Source.Keys[KeyIndex].Value) * DecimalRate);
		}

		if (bNegateTangents)
		{
			ArriveTangent = -ArriveTangent;
			LeaveTangent = -LeaveTangent;
		}
		
		FFrameNumber KeyTime = (Source.Keys[KeyIndex].Value * DestFrameRate).RoundToFrame();
		if (ChannelData.FindKey(KeyTime) == INDEX_NONE)
		{
			FMovieSceneFloatValue NewKey(Source.Keys[KeyIndex].Value);

			NewKey.InterpMode = Source.Keys[KeyIndex].InterpMode;
			NewKey.TangentMode = Source.Keys[KeyIndex].TangentMode;
			NewKey.Tangent.ArriveTangent = ArriveTangent / DestFrameRate.AsDecimal();
			NewKey.Tangent.LeaveTangent = LeaveTangent / DestFrameRate.AsDecimal();
			NewKey.Tangent.TangentWeightMode = RCTWM_WeightedNone;
			NewKey.Tangent.ArriveTangentWeight = 0.0f;
			NewKey.Tangent.LeaveTangentWeight = 0.0f;
			ChannelData.AddKey(KeyTime, NewKey);
		}
	}

	Dest->AutoSetTangents();
}
#else
static void ImportTransformChannel(const FInterpCurveFloat& Source, FMovieSceneFloatChannel* Dest, FFrameRate DestFrameRate, bool bNegateTangents)
{
	TMovieSceneChannelData<FMovieSceneFloatValue> ChannelData = Dest->GetData();
	ChannelData.Reset();
	double DecimalRate = DestFrameRate.AsDecimal();
	for (int32 KeyIndex = 0; KeyIndex < Source.Points.Num(); ++KeyIndex)
	{
		float ArriveTangent = Source.Points[KeyIndex].ArriveTangent;
		if (KeyIndex > 0)
		{
			ArriveTangent = ArriveTangent / ((Source.Points[KeyIndex].InVal - Source.Points[KeyIndex - 1].InVal) * DecimalRate);
		}

		float LeaveTangent = Source.Points[KeyIndex].LeaveTangent;
		if (KeyIndex < Source.Points.Num() - 1)
		{
			LeaveTangent = LeaveTangent / ((Source.Points[KeyIndex + 1].InVal - Source.Points[KeyIndex].InVal) * DecimalRate);
		}

		if (bNegateTangents)
		{
			ArriveTangent = -ArriveTangent;
			LeaveTangent = -LeaveTangent;
		}

		FFrameNumber KeyTime = (Source.Points[KeyIndex].InVal * DestFrameRate).RoundToFrame();
#if ENGINE_MINOR_VERSION > 20
		FMatineeImportTools::SetOrAddKey(ChannelData, KeyTime, Source.Points[KeyIndex].OutVal, ArriveTangent, LeaveTangent, Source.Points[KeyIndex].InterpMode, DestFrameRate);
#else
		FMatineeImportTools::SetOrAddKey(ChannelData, KeyTime, Source.Points[KeyIndex].OutVal, ArriveTangent, LeaveTangent, Source.Points[KeyIndex].InterpMode);
#endif
	}

	Dest->AutoSetTangents();
}
#endif

static bool ImportFBXTransform(FString NodeName, UMovieScene3DTransformSection* TransformSection, UnFbx::FFbxCurvesAPI& CurveAPI)
{


	// Look for transforms explicitly
	FTransform DefaultTransform;

// #if ENGINE_MINOR_VERSION > 21
#if ENGINE_MINOR_VERSION < 21
	FRichCurve Translation[3];
	FRichCurve EulerRotation[3];
	FRichCurve Scale[3];
#else
	FInterpCurveFloat Translation[3];
	FInterpCurveFloat EulerRotation[3];
	FInterpCurveFloat Scale[3];
#endif
	CurveAPI.GetConvertedTransformCurveData(NodeName, Translation[0], Translation[1], Translation[2], EulerRotation[0], EulerRotation[1], EulerRotation[2], Scale[0], Scale[1], Scale[2], DefaultTransform);


	TransformSection->Modify();

	FFrameRate FrameRate = TransformSection->GetTypedOuter<UMovieScene>()->GetTickResolution();


	FVector Location = DefaultTransform.GetLocation(), Rotation = DefaultTransform.GetRotation().Euler(), Scale3D = DefaultTransform.GetScale3D();

	TArrayView<FMovieSceneFloatChannel*> Channels = TransformSection->GetChannelProxy().GetChannels<FMovieSceneFloatChannel>();

	Channels[0]->SetDefault(Location.X);
	Channels[1]->SetDefault(Location.Y);
	Channels[2]->SetDefault(Location.Z);

	Channels[3]->SetDefault(Rotation.X);
	Channels[4]->SetDefault(Rotation.Y);
	Channels[5]->SetDefault(Rotation.Z);

	Channels[6]->SetDefault(Scale3D.X);
	Channels[7]->SetDefault(Scale3D.Y);
	Channels[8]->SetDefault(Scale3D.Z);

	ImportTransformChannel(Translation[0], Channels[0], FrameRate, false);
	ImportTransformChannel(Translation[1], Channels[1], FrameRate, true);
	ImportTransformChannel(Translation[2], Channels[2], FrameRate, false);

	ImportTransformChannel(EulerRotation[0], Channels[3], FrameRate, false);
	ImportTransformChannel(EulerRotation[1], Channels[4], FrameRate, true);
	ImportTransformChannel(EulerRotation[2], Channels[5], FrameRate, true);

	ImportTransformChannel(Scale[0], Channels[6], FrameRate, false);
	ImportTransformChannel(Scale[1], Channels[7], FrameRate, false);
	ImportTransformChannel(Scale[2], Channels[8], FrameRate, false);

	return true;
}

void GetCameras( FbxNode* Parent, TArray<FbxCamera*>& Cameras )
{
	FbxCamera* Camera = Parent->GetCamera();
	if( Camera )
	{
		Cameras.Add(Camera);
	}

	int32 NodeCount = Parent->GetChildCount();
	for ( int32 NodeIndex = 0; NodeIndex < NodeCount; ++NodeIndex )
	{
		FbxNode* Child = Parent->GetChild( NodeIndex );
		GetCameras(Child, Cameras);
	}
}
bool USmartMovieScene3DTransformSectionExtensions::SmImportCameraFbx(UMovieScene3DTransformSection* Section, FString FbxFileName)
{
	UnFbx::FFbxImporter* FbxImporter = UnFbx::FFbxImporter::GetInstance();

	UnFbx::FBXImportOptions* ImportOptions = FbxImporter->GetImportOptions();
	bool bConverteScene = ImportOptions->bConvertScene;
	bool bConverteSceneUnit = ImportOptions->bConvertSceneUnit;
	bool bForceFrontXAxis = ImportOptions->bForceFrontXAxis;

	ImportOptions->bConvertScene = true;
	ImportOptions->bConvertSceneUnit = true;
	// ImportOptions->bForceFrontXAxis = py_force_front_x_axis && PyObject_IsTrue(py_force_front_x_axis);

	const FString Extension = FPaths::GetExtension(FbxFileName);
	if (!FbxImporter->ImportFromFile(FbxFileName, Extension, true))
	{
		FbxImporter->ReleaseScene();
		ImportOptions->bConvertScene = bConverteScene;
		ImportOptions->bConvertSceneUnit = bConverteSceneUnit;
		ImportOptions->bForceFrontXAxis = bForceFrontXAxis;
		// return PyErr_Format(PyExc_Exception, "unable to import Fbx file");
		return false;
	}

	UnFbx::FFbxCurvesAPI CurveAPI;
	FbxImporter->PopulateAnimatedCurveData(CurveAPI);

	TArray<FString> AllNodeNames;
#if ENGINE_MINOR_VERSION < 18
	CurveAPI.GetAnimatedNodeNameArray(AllNodeNames);
#else
	CurveAPI.GetAllNodeNameArray(AllNodeNames);
#endif
	// GLog->Log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
	// FbxCamera::eFocalLength
	// FbxCamera* Camera = FbxImporter->Scene->GetRootNode()->GetCamera()->;
	TArray<FbxCamera*> AllCameras;
	GetCameras(FbxImporter->Scene->GetRootNode(), AllCameras);
	// FbxNode* Node = FbxImporter->Scene->GetRootNode();
	// FbxCamera* Camera = Node->GetCamera();
	if( AllCameras.Num() != 1 )
	{
		return false;
	}
	FString FbxNodeName = AllCameras[0]->GetName();
	GLog->Log(FbxNodeName);
	
	bool bIsSuccess=false;
	for (FString NodeName : AllNodeNames)
	{
		if (NodeName != FbxNodeName)
			continue;

		// Look for transforms explicitly
// #if ENGINE_MINOR_VERSION > 21
#if ENGINE_MINOR_VERSION < 21
		FRichCurve Translation[3];
		FRichCurve EulerRotation[3];
		FRichCurve Scale[3];
#else
		FInterpCurveFloat Translation[3];
		FInterpCurveFloat EulerRotation[3];
		FInterpCurveFloat Scale[3];
#endif
		FTransform DefaultTransform;
#if ENGINE_MINOR_VERSION >= 18
		CurveAPI.GetConvertedTransformCurveData(NodeName, Translation[0], Translation[1], Translation[2], EulerRotation[0], EulerRotation[1], EulerRotation[2], Scale[0], Scale[1], Scale[2], DefaultTransform);
#if ENGINE_MINOR_VERSION < 20
		for (int32 ChannelIndex = 0; ChannelIndex < 3; ++ChannelIndex)
		{
			EAxis::Type ChannelAxis = EAxis::X;
			if (ChannelIndex == 1)
			{
				ChannelAxis = EAxis::Y;
			}
			else if (ChannelIndex == 2)
			{
				ChannelAxis = EAxis::Z;
			}

			section->GetTranslationCurve(ChannelAxis).SetDefaultValue(DefaultTransform.GetLocation()[ChannelIndex]);
			section->GetRotationCurve(ChannelAxis).SetDefaultValue(DefaultTransform.GetRotation().Euler()[ChannelIndex]);
			section->GetScaleCurve(ChannelAxis).SetDefaultValue(DefaultTransform.GetScale3D()[ChannelIndex]);
		}
#endif

#else
		CurveAPI.GetConvertedTransformCurveData(NodeName, Translation[0], Translation[1], Translation[2], EulerRotation[0], EulerRotation[1], EulerRotation[2], Scale[0], Scale[1], Scale[2]);

#endif
#if ENGINE_MINOR_VERSION < 20
		float MinTime = FLT_MAX;
		float MaxTime = -FLT_MAX;

		const int NumCurves = 3; // Trans, Rot, Scale
		for (int32 CurveIndex = 0; CurveIndex < NumCurves; ++CurveIndex)
		{
			for (int32 ChannelIndex = 0; ChannelIndex < 3; ++ChannelIndex)
			{
				EAxis::Type ChannelAxis = EAxis::X;
				if (ChannelIndex == 1)
				{
					ChannelAxis = EAxis::Y;
				}
				else if (ChannelIndex == 2)
				{
					ChannelAxis = EAxis::Z;
				}

				FInterpCurveFloat* CurveFloat = nullptr;
				FRichCurve* ChannelCurve = nullptr;
				bool bNegative = false;

				if (CurveIndex == 0)
				{
					CurveFloat = &Translation[ChannelIndex];
					ChannelCurve = &section->GetTranslationCurve(ChannelAxis);
					if (ChannelIndex == 1)
					{
						bNegative = true;
					}
				}
				else if (CurveIndex == 1)
				{
					CurveFloat = &EulerRotation[ChannelIndex];
					ChannelCurve = &section->GetRotationCurve(ChannelAxis);
					if (ChannelIndex == 1 || ChannelIndex == 2)
					{
						bNegative = true;
					}
				}
				else if (CurveIndex == 2)
				{
					CurveFloat = &Scale[ChannelIndex];
					ChannelCurve = &section->GetScaleCurve(ChannelAxis);
				}

				if (ChannelCurve != nullptr && CurveFloat != nullptr)
				{
					ChannelCurve->Reset();

					for (int32 KeyIndex = 0; KeyIndex < CurveFloat->Points.Num(); ++KeyIndex)
					{
						MinTime = FMath::Min(MinTime, CurveFloat->Points[KeyIndex].InVal);
						MaxTime = FMath::Max(MaxTime, CurveFloat->Points[KeyIndex].InVal);

						float ArriveTangent = CurveFloat->Points[KeyIndex].ArriveTangent;
						if (KeyIndex > 0)
						{
							ArriveTangent = ArriveTangent / (CurveFloat->Points[KeyIndex].InVal - CurveFloat->Points[KeyIndex - 1].InVal);
						}

						float LeaveTangent = CurveFloat->Points[KeyIndex].LeaveTangent;
						if (KeyIndex < CurveFloat->Points.Num() - 1)
						{
							LeaveTangent = LeaveTangent / (CurveFloat->Points[KeyIndex + 1].InVal - CurveFloat->Points[KeyIndex].InVal);
						}

						if (bNegative)
						{
							ArriveTangent = -ArriveTangent;
							LeaveTangent = -LeaveTangent;
						}

						FMatineeImportTools::SetOrAddKey(*ChannelCurve, CurveFloat->Points[KeyIndex].InVal, CurveFloat->Points[KeyIndex].OutVal, ArriveTangent, LeaveTangent, CurveFloat->Points[KeyIndex].InterpMode);

			}

					ChannelCurve->RemoveRedundantKeys(KINDA_SMALL_NUMBER);
					ChannelCurve->AutoSetTangents();
				}
			}
		}

#else
		ImportFBXTransform(FbxNodeName, Section, CurveAPI);
#endif

#if ENGINE_MINOR_VERSION < 20
		Section->SetStartTime(MinTime);
		section->SetEndTime(MaxTime);
#endif

		FbxImporter->ReleaseScene();
		ImportOptions->bConvertScene = bConverteScene;
		ImportOptions->bConvertSceneUnit = bConverteSceneUnit;
		ImportOptions->bForceFrontXAxis = bForceFrontXAxis;
		// Py_RETURN_NONE;
		bIsSuccess = true;
	}

	FbxImporter->ReleaseScene();
	ImportOptions->bConvertScene = bConverteScene;
	ImportOptions->bConvertSceneUnit = bConverteSceneUnit;
	ImportOptions->bForceFrontXAxis = bForceFrontXAxis;
	// return PyErr_Format(PyExc_Exception, "unable to find specified node in Fbx file");
	return bIsSuccess;

}

